package top.huanyv.tools.jwt;

import top.huanyv.tools.bean.Digest;
import top.huanyv.tools.utils.Assert;
import top.huanyv.tools.utils.EncodeUtil;
import top.huanyv.tools.utils.JsonUtil;
import top.huanyv.tools.utils.StringUtil;

import java.nio.charset.StandardCharsets;

/**
 * @author huanyv
 * @date 2023/1/31 16:31
 */
public class Jwt {

    private static final String DEFAULT_SALT = "Hello World!";

    // 一天
    private static final long DEFAULT_EXP = 60 * 60 * 24;

    private final String header = "{\"alg\": \"HS256\", \"typ\": \"JWT\"}";

    private Payload payload;

    public Jwt(String sub) {
        this(sub, DEFAULT_EXP);
    }

    public Jwt(String sub, long time) {
        payload = new Payload(sub);
        long currentTimeMillis = currentUnix();
        payload.setIat(currentTimeMillis);
        payload.setExp(currentTimeMillis + time);
    }

    public Jwt(Payload payload) {
        this.payload = payload;
    }

    /**
     * 使用默认盐值解析JWT
     *
     * @param jwt jwt
     * @return {@link Payload}
     * @throws JwtParseException jwt解析异常
     */
    public static Payload parse(String jwt) throws JwtParseException {
       return parse(jwt, DEFAULT_SALT);
    }

    /**
     * 使用指定盐值解析JWT
     *
     * @param jwt  jwt
     * @param salt 盐
     * @return {@link Payload}
     * @throws JwtParseException jwt解析异常
     */
    public static Payload parse(String jwt, String salt) throws JwtParseException {
        Assert.isTrue(StringUtil.hasText(jwt), "JWT String argument cannot be null or empty.");
        String[] strings = jwt.split("\\.");
        if (strings.length != 3) {
            throw new JwtParseException("JWT format is incorrect.");
        }
        String header = new String(EncodeUtil.decodeBASE64(strings[0]), StandardCharsets.UTF_8);
        String pl = new String(EncodeUtil.decodeBASE64(strings[1]), StandardCharsets.UTF_8);
        String s = hash(header, pl, salt);
        if (!s.equals(strings[2])) {
            throw new JwtParseException("JWT is illegal.");
        }
        Payload payload = JsonUtil.fromJson(pl, Payload.class);
        if (payload == null) {
            throw new JwtParseException("'" + pl + "' is not a json.");
        }
        // 当前时间
        long currentUnix = currentUnix();
        // 可用时间判定
        long nbf = payload.getNbf();
        if (nbf > currentUnix) {
            throw new JwtParseException("JWT is not available.");
        }
        // 过期时间判定
        long exp = payload.getExp();
        if (exp < payload.getIat()) {
            // 过期时间大于签发时间，不合法
            throw new JwtParseException("JWT is illegal.");
        }
        if (exp < currentUnix) {
            throw new JwtParseException("JWT has expired.");
        }
        return payload;
    }

    public Payload getPayload() {
        return payload;
    }

    /**
     * 获得默认盐值加密后的Token
     *
     * @return {@link String}
     */
    public String getToken() {
        return getToken(DEFAULT_SALT);
    }

    /**
     * 获得指定盐值加密后的Token
     *
     * @param salt 盐
     * @return {@link String}
     */
    public String getToken(String salt) {
        return EncodeUtil.encodeBASE64(this.header) + "."
                + EncodeUtil.encodeBASE64(this.payload.toString()) + "."
                + hash(header, payload.toString(), salt);
    }

    @Override
    public String toString() {
        return getToken();
    }

    /**
     * signature部分加密算法
     *
     * @param header  头
     * @param payload 有效载荷
     * @param salt    盐
     * @return {@link String}
     */
    private static String hash(String header, String payload, String salt) {
        // 先MD5一次，再SHA256一次
        salt = Digest.md5Digest(salt);
        Digest digest = new Digest(header + payload + salt, Digest.SHA_256);
        return digest.toString();
    }

    /**
     * 获取当前unix时间戳
     *
     * @return long
     */
    private static long currentUnix() {
        return Math.round((double) System.currentTimeMillis() / 1000);
    }
}
